#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SIMION多线程工作空间管理模块

解决多线程运行SIMION时的临时文件冲突问题
为每个线程创建独立的工作目录，避免文件冲突
"""

import os
import shutil
import tempfile
import threading
from pathlib import Path
import platform
from utils.logging import get_logger
import time
from datetime import datetime

logger = get_logger('simion_workspace')


class SimionWorkspaceManager:
    """SIMION工作空间管理器"""
    
    def __init__(self, base_iob_path, thread_count=None, lua_path=None):
        """
        初始化工作空间管理器
        
        参数:
            base_iob_path: 基础IOB文件路径
            thread_count: 线程数量，默认为CPU核心数
            lua_path: Lua文件路径（可选）
        """
        self.base_iob_path = Path(base_iob_path)
        self.base_dir = self.base_iob_path.parent
        self.iob_name = self.base_iob_path.stem
        # 确保线程数不超过实际需要的数量
        self.thread_count = thread_count or os.cpu_count()
        self.lua_path = Path(lua_path) if lua_path and lua_path != 'None' else None
        
        # 工作空间目录
        self.workspace_dir = self.base_dir / "simion_workspace"
        self.common_dir = self.workspace_dir / "common"
        self.runs_dir = self.workspace_dir / "runs"
        
        # 线程锁
        self._lock = threading.Lock()
        self._created_dirs = set()
    
    def setup_workspace(self):
        """设置工作空间目录结构"""
        try:
            # 先清理旧的工作空间（如果存在）
            if self.workspace_dir.exists():
                logger.info("清理旧的工作空间...")
                try:
                    shutil.rmtree(self.workspace_dir)
                except PermissionError as e:
                    logger.warning(f"无法删除工作空间目录，可能被其他进程占用: {e}")
                    logger.info("尝试强制清理...")
                    self._force_cleanup_workspace()
                except Exception as e:
                    logger.warning(f"清理工作空间失败: {e}")
                    logger.info("尝试强制清理...")
                    self._force_cleanup_workspace()
            
            # 创建基础目录
            self.workspace_dir.mkdir(exist_ok=True)
            self.common_dir.mkdir(exist_ok=True)
            self.runs_dir.mkdir(exist_ok=True)
            
            # 复制PA文件到common目录
            self._copy_pa_files()
            
            # 为每个线程创建独立目录
            logger.info(f"正在创建 {self.thread_count} 个线程工作目录...")
            for i in range(1, self.thread_count + 1):
                self._create_thread_directory(i)
            
            # 等待所有文件复制完成，避免异步I/O竞争条件
            self._wait_for_all_files_ready()
            
            logger.info(f"工作空间设置完成")
            return True
            
        except Exception as e:
            logger.error(f"设置工作空间失败: {e}")
            return False
    
    def _wait_for_all_files_ready(self):
        """等待所有线程目录的文件复制完成，避免异步I/O竞争条件"""        
        max_wait = 30 
        
        for attempt in range(max_wait * 10):
            all_ready = True
            missing_files = []
            
            for i in range(1, self.thread_count + 1):
                run_dir = self.runs_dir / f"run{i}"
                lua_file = run_dir / f"{self.iob_name}.lua"
                iob_file = run_dir / f"{self.iob_name}.iob"
                
                if not lua_file.exists():
                    all_ready = False
                    missing_files.append(f"run{i}/lua")
                if not iob_file.exists():
                    all_ready = False
                    missing_files.append(f"run{i}/iob")
            
            if all_ready:
                return True
            
            time.sleep(0.1) 
        
        logger.warning(f"部分文件未及时复制完成，可能导致部分线程执行失败")
        logger.warning(f"缺少的文件: {', '.join(missing_files)}")
        return False
    
    def _force_cleanup_workspace(self):
        """强制清理工作空间"""
        try:
            time.sleep(2)
            
            if self.workspace_dir.exists():
                shutil.rmtree(self.workspace_dir)
                logger.info("强制清理成功")
            
        except Exception as e:
            logger.error(f"强制清理失败: {e}")
            try:
                if self.workspace_dir.exists():
                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                    old_workspace = self.workspace_dir.parent / f"simion_workspace_old_{timestamp}"
                    self.workspace_dir.rename(old_workspace)
                    logger.info(f"重命名旧工作空间为: {old_workspace}")
            except Exception as rename_e:
                logger.error(f"重命名工作空间也失败: {rename_e}")
    
    def _copy_pa_files(self):
        """复制PA文件到common目录"""
        pa_files = list(self.base_dir.glob(f"{self.iob_name}.pa*"))
        
        if pa_files:
            for pa_file in pa_files:
                target_file = self.common_dir / pa_file.name
                if not target_file.exists():
                    try:
                        shutil.copy2(pa_file, target_file)
                    except Exception as e:
                        logger.error(f"复制PA文件失败 {pa_file.name}: {e}")
    
    def _batch_copy_lua_files(self):
        """批量复制Lua文件到所有线程目录，使用原子写入避免竞争条件"""
        try:
            if self.lua_path and str(self.lua_path) != 'None' and self.lua_path.exists():
                lua_file = self.lua_path
            else:
                lua_file = self.base_dir / f"{self.iob_name}.lua"
            
            if not lua_file.exists():
                logger.warning(f"Lua文件不存在: {lua_file}")
                return False
            
            with open(lua_file, 'rb') as f:
                lua_content = f.read()
            
            success_count = 0   
            for i in range(1, self.thread_count + 1):
                try:
                    run_dir = self.runs_dir / f"run{i}"
                    target_lua = run_dir / lua_file.name
                    tmp_lua = run_dir / (lua_file.stem + ".tmp")
                    
                    with self._lock:
                        with open(tmp_lua, 'wb') as f:
                            f.write(lua_content)
                            f.flush()
                            os.fsync(f.fileno()) 
                        
                        tmp_lua.rename(target_lua)
                    
                    success_count += 1
                    
                except Exception as e:
                    logger.error(f"复制Lua文件失败 run{i}: {e}")
            
            return success_count == self.thread_count
            
        except Exception as e:
            logger.error(f"批量复制Lua文件失败: {e}")
            return False

    def _create_thread_directory_files(self, thread_id):
        """为指定线程目录复制其他文件（IOB、FLY2等），不复制Lua文件（已在批量复制中完成）"""
        try:
            run_dir = self.runs_dir / f"run{thread_id}"
            self._copy_iob_file(thread_id, run_dir)
            self._copy_fly2_file(thread_id, run_dir)
            self._copy_excellib_file(thread_id, run_dir)
            self._create_pa_symlinks(thread_id, run_dir) 
            return True      
        except Exception as e:
            logger.error(f"创建线程目录文件失败 run{thread_id}: {e}")
            return False

    def _create_thread_directory(self, thread_id):
        """为指定线程创建独立目录"""
        run_dir = self.runs_dir / f"run{thread_id}"
        run_dir.mkdir(exist_ok=True)
        output_dir = run_dir / "output"
        output_dir.mkdir(exist_ok=True)
        iob_file = run_dir / f"{self.iob_name}.iob"
        if not iob_file.exists():
            try:
                with self._lock:  
                    with open(self.base_iob_path, 'rb') as src_f:
                        data = src_f.read()
                    with open(iob_file, 'wb') as dst_f:
                        dst_f.write(data)
                        os.fsync(dst_f.fileno())
            except Exception as e:
                logger.error(f"复制IOB文件失败 run{thread_id}: {e}")
        
        # 复制FLY2文件（如果存在）- 使用编码处理方法
        self._copy_fly2_file(thread_id, run_dir)
        
        # 复制Lua文件（如果存在） 线程锁保护文件复制
        lua_file = self.base_dir / f"{self.iob_name}.lua"
        if lua_file.exists():
            target_lua = run_dir / f"{self.iob_name}.lua"
        
        if lua_file.exists():
            try:
                with self._lock:  
                    with open(lua_file, 'rb') as src_f:
                        data = src_f.read()
                    with open(target_lua, 'wb') as dst_f:
                        dst_f.write(data)
                        os.fsync(dst_f.fileno())
            except Exception as e:
                logger.error(f"复制Lua文件失败 run{thread_id}: {e}")
        else:
            logger.warning(f"Lua文件不存在: {lua_file}")
        
        # 复制excellib.lua文件（如果存在） 线程锁保护文件复制
        excellib_file = self.base_dir / "excellib.lua"
        if excellib_file.exists():
            target_excellib = run_dir / "excellib.lua"
            if not target_excellib.exists():
                try:
                    with self._lock:  
                        with open(excellib_file, 'rb') as src_f:
                            data = src_f.read()
                        with open(target_excellib, 'wb') as dst_f:
                            dst_f.write(data)
                            os.fsync(dst_f.fileno())
                except Exception as e:
                    logger.error(f"复制excellib.lua失败 run{thread_id}: {e}")
        
        # 创建PA文件的符号链接（Windows）或硬链接（Linux/Mac）
        self._create_pa_links(run_dir)
    
    def _create_pa_links(self, run_dir):
        """为PA文件创建链接"""
        pa_files = list(self.common_dir.glob(f"{self.iob_name}.pa*"))
        
        for pa_file in pa_files:
            target_link = run_dir / pa_file.name
            if not target_link.exists():
                try:
                    if platform.system() == "Windows":
                        # Windows使用硬链接
                        os.link(str(pa_file), str(target_link))
                    else:
                        # Linux/Mac使用符号链接
                        os.symlink(str(pa_file), str(target_link))
                except Exception as e:
                    logger.error(f"创建PA链接失败 {pa_file.name}: {e}")
    
    def get_thread_workspace(self, thread_id):
        """获取指定线程的工作空间路径"""
        run_dir = self.runs_dir / f"run{thread_id}"
        return {
            'iob_path': str(run_dir / f"{self.iob_name}.iob"),
            'output_dir': str(run_dir / "output"),
            'work_dir': str(run_dir)
        }
    
    def cleanup_workspace(self, keep_workspace=False, force=False):
        """
        清理SIMION工作空间
        
        参数:
            keep_workspace (bool): 是否保留目录结构和文件
            force (bool): 是否强制清理（忽略文件锁定）
        """
        try:
            if not self.workspace_dir.exists():
                return
            
            # 计算清理前的磁盘使用量
            total_size = self._get_directory_size(self.workspace_dir)
            
            if keep_workspace:
                self._cleanup_simion_temp_files(verbose=False)
                logger.info(f"工作空间临时文件已清理，目录结构已保留: {self.workspace_dir}")
            else:
                logger.info("删除整个工作空间目录")
                if force:
                    self._force_cleanup_workspace()
                else:
                    shutil.rmtree(self.workspace_dir, ignore_errors=True)
                
                if total_size > 0:
                    size_str = self._format_size(total_size)
                    logger.info(f"工作空间已完全清理，释放磁盘空间: {size_str}")
                else:
                    logger.info("工作空间已完全清理（目录为空或不存在）")
        except Exception as e:
            logger.error(f"清理工作空间失败: {e}")
            if force:
                logger.info("尝试强制清理...")
                self._force_cleanup_workspace()
    
    def _cleanup_simion_temp_files(self, verbose=False):
        """清理SIMION生成的临时文件"""
        try:
            # 基础临时文件扩展名（不包含PA文件）
            base_temp_extensions = [
                '.tmp', '.wrk', '.log', '.out', '.err', '.cache', '.lock'
            ]
            
            # 动态获取PA临时文件扩展名
            pa_temp_extensions = self._get_pa_temp_extensions()
            all_temp_extensions = base_temp_extensions + pa_temp_extensions
            if self.runs_dir.exists():
                for run_dir in self.runs_dir.iterdir():
                    if run_dir.is_dir():
                        self._cleanup_directory_temp_files(run_dir, all_temp_extensions, verbose)
            
            if self.common_dir.exists():
                self._cleanup_directory_temp_files(self.common_dir, all_temp_extensions, verbose)     
        except Exception as e:
            logger.error(f"清理SIMION临时文件失败: {e}")
    
    def _get_pa_temp_extensions(self):
        """动态获取PA临时文件扩展名"""
        pa_extensions = []
        
        try:
            # 检查原始目录中的PA文件，确定电极数量
            base_dir = Path(self.base_iob_path).parent
            iob_name = self.base_iob_path.stem
            pa_files = list(base_dir.glob(f"{iob_name}.pa*"))
            for pa_file in pa_files:
                suffix = pa_file.suffix
                if suffix and suffix not in ['.pa']:  
                    pa_extensions.append(suffix)
    
            if not pa_extensions:
                pa_extensions = ['.pa#', '.pa0', '.pa1', '.pa2']
            return pa_extensions
        except Exception as e:
            logger.error(f"获取PA临时文件扩展名失败: {e}")
            return ['.pa#', '.pa0', '.pa1', '.pa2']
    
    def get_electrode_count(self):
        """获取电极数量"""
        try:
            base_dir = Path(self.base_iob_path).parent
            iob_name = self.base_iob_path.stem
            pa_files = list(base_dir.glob(f"{iob_name}.pa*"))
            electrode_count = len([f for f in pa_files if f.suffix != '.pa'])
            logger.info(f"检测到电极数量: {electrode_count}")
            return electrode_count 
        except Exception as e:
            logger.error(f"获取电极数量失败: {e}")
            return 0
    
    def _cleanup_directory_temp_files(self, directory, temp_extensions, verbose=False):
        """清理指定目录中的临时文件"""
        try:
            for file_path in directory.iterdir():
                if file_path.is_file():
                    if any(file_path.suffix.lower() == ext for ext in temp_extensions):
                        if file_path.suffix.lower().startswith('.pa'):
                            if self._is_pa_temp_file(file_path):
                                try:
                                    file_path.unlink()
                                    if verbose:
                                        logger.info(f"删除PA临时文件: {file_path.name}")
                                except Exception as e:
                                    if verbose:
                                        logger.error(f"删除PA临时文件失败 {file_path.name}: {e}")
                        else:
                            # 其他临时文件直接删除
                            try:
                                file_path.unlink()
                                if verbose:
                                    logger.info(f"删除临时文件: {file_path.name}")
                            except Exception as e:
                                if verbose:
                                    logger.error(f"删除临时文件失败 {file_path.name}: {e}")
                    # 检查是否是SIMION生成的临时文件（以特定前缀命名）
                    elif file_path.name.startswith(('temp_', 'simion_', 'fly_')):
                        try:
                            file_path.unlink()
                            if verbose:
                                logger.info(f"删除SIMION临时文件: {file_path.name}")
                        except Exception as e:
                            if verbose:
                                logger.error(f"删除SIMION临时文件失败 {file_path.name}: {e}")
        except Exception as e:
            if verbose:
                logger.error(f"清理目录临时文件失败 {directory}: {e}")
    
    def _is_pa_temp_file(self, file_path):
        """判断是否是PA临时文件（而非原始PA文件）"""
        try:
            if file_path.parent == self.common_dir:
                return False  
            
            # 检查文件是否是链接文件
            if file_path.is_symlink() or (platform.system() == "Windows" and self._is_hardlink(file_path)):
                return False  # 链接文件指向原始文件，不删除
            return True
        except Exception:
            return True

    def _copy_iob_file(self, thread_id, run_dir):
        """复制IOB文件到指定线程目录"""
        target_iob = run_dir / f"{self.iob_name}.iob"
        try:
            with self._lock: 
                with open(self.base_iob_path, 'rb') as src_f:
                    data = src_f.read()
                with open(target_iob, 'wb') as dst_f:
                    dst_f.write(data)
                    os.fsync(dst_f.fileno())
        except Exception as e:
            logger.error(f"复制IOB文件失败 run{thread_id}: {e}")

    def _copy_fly2_file(self, thread_id, run_dir):
        """复制FLY2文件到指定线程目录，处理编码问题"""
        fly2_file = self.base_dir / f"{self.iob_name}.fly2"
        if fly2_file.exists():
            target_fly2 = run_dir / f"{self.iob_name}.fly2"
            try:
                with self._lock:
                    # 首先检查文件是否为文本文件（通过读取前几行判断）
                    try:
                        with open(fly2_file, 'rb') as f:
                            first_bytes = f.read(512)
                            # 检查是否包含NULL字节（二进制文件的特征）
                            is_binary = b'\x00' in first_bytes
                    except Exception:
                        is_binary = False
                    
                    if is_binary:
                        # 二进制文件：直接复制
                        with open(fly2_file, 'rb') as src_f:
                            data = src_f.read()
                        with open(target_fly2, 'wb') as dst_f:
                            dst_f.write(data)
                            os.fsync(dst_f.fileno())
                        logger.debug(f"FLY2文件（二进制）复制成功 run{thread_id}")
                    else:
                        # 文本文件：需要处理编码
                        encodings = ['utf-8', 'utf-8-sig', 'gbk', 'latin-1', 'cp1252']
                        content = None
                        used_encoding = None
                        
                        for encoding in encodings:
                            try:
                                with open(fly2_file, 'r', encoding=encoding, errors='strict') as src_f:
                                    content = src_f.read()
                                used_encoding = encoding
                                break
                            except (UnicodeDecodeError, UnicodeError):
                                continue
                        
                        # 如果所有编码都失败，使用二进制读取并尝试修复
                        if content is None:
                            with open(fly2_file, 'rb') as src_f:
                                raw_data = src_f.read()
                            # 移除BOM
                            if raw_data.startswith(b'\xef\xbb\xbf'):
                                raw_data = raw_data[3:]
                            # 尝试以UTF-8解码，替换无法解码的字节
                            content = raw_data.decode('utf-8', errors='replace')
                            used_encoding = 'utf-8 (replaced)'
                        
                        # 清理可能导致Lua解析错误的字符
                        if content:
                            lines = content.splitlines(keepends=False)
                            cleaned_lines = []
                            for line_num, line in enumerate(lines, 1):
                                # 清理每行，特别是第9行需要更严格的清理
                                is_line_9 = (line_num == 9)
                                cleaned_chars = []
                                
                                for char in line:
                                    ord_char = ord(char)
                                    # 对于第9行，只允许纯ASCII字符（更严格）
                                    # 对于其他行，允许扩展ASCII字符
                                    if is_line_9:
                                        # 第9行：只允许可打印ASCII（32-126）、制表符、空格
                                        if (32 <= ord_char <= 126) or ord_char == 9:
                                            cleaned_chars.append(char)
                                        elif ord_char == 0:  # NULL字符
                                            cleaned_chars.append(' ')
                                        else:
                                            # 第9行的任何非ASCII字符都替换为空格
                                            cleaned_chars.append(' ')
                                    else:
                                        # 其他行：允许ASCII和常见扩展字符
                                        if (32 <= ord_char <= 126) or ord_char in (9, 10, 13):
                                            cleaned_chars.append(char)
                                        elif ord_char == 0:  # NULL字符必须移除
                                            cleaned_chars.append(' ')
                                        elif 0 < ord_char < 32 and ord_char not in (9, 10, 13):  # 控制字符
                                            cleaned_chars.append(' ')
                                        elif 128 <= ord_char <= 255:
                                            # 扩展ASCII字符：尝试保留，但SIMION可能不支持，所以也替换为空格
                                            cleaned_chars.append(' ')
                                        else:
                                            # Unicode字符：替换为空格
                                            cleaned_chars.append(' ')
                                
                                cleaned_line = ''.join(cleaned_chars)
                                
                                # 如果第9行有非ASCII字符，记录警告
                                if is_line_9:
                                    original_has_non_ascii = any(ord(c) > 127 or (ord(c) < 32 and ord(c) not in (9, 10, 13)) for c in line)
                                    if original_has_non_ascii:
                                        logger.warning(f"FLY2文件第9行包含非ASCII或控制字符，已清理为纯ASCII: {fly2_file}")
                                        logger.debug(f"第9行原始内容（前50字符）: {repr(line[:50])}")
                                        logger.debug(f"第9行清理后内容（前50字符）: {repr(cleaned_line[:50])}")
                                
                                cleaned_lines.append(cleaned_line)
                            
                            # 使用LF作为行结束符（Unix风格），SIMION通常支持
                            content = '\n'.join(cleaned_lines)
                        
                        # 以ASCII编码写入（而不是UTF-8），因为SIMION的Lua解析器可能只支持ASCII
                        # 这样可以确保所有字符都是可打印的ASCII
                        try:
                            # 先尝试将内容编码为ASCII（会抛出错误如果有非ASCII字符）
                            ascii_content = content.encode('ascii', errors='replace').decode('ascii')
                            with open(target_fly2, 'w', encoding='ascii', newline='\n') as dst_f:
                                dst_f.write(ascii_content)
                                dst_f.flush()
                                os.fsync(dst_f.fileno())
                        except Exception as ascii_e:
                            # 如果ASCII编码失败，使用UTF-8但确保替换所有错误
                            logger.warning(f"FLY2文件包含非ASCII字符，使用UTF-8编码并替换错误: {ascii_e}")
                            with open(target_fly2, 'w', encoding='utf-8', newline='\n', errors='replace') as dst_f:
                                dst_f.write(content)
                                dst_f.flush()
                                os.fsync(dst_f.fileno())
                        
                        if used_encoding:
                            logger.debug(f"FLY2文件（文本）复制成功 run{thread_id}，使用编码: {used_encoding}")
                        
            except Exception as e:
                logger.error(f"复制FLY2文件失败 run{thread_id}: {e}")
                # 最后的备选方案：纯二进制复制
                try:
                    with self._lock:
                        with open(fly2_file, 'rb') as src_f:
                            data = src_f.read()
                        with open(target_fly2, 'wb') as dst_f:
                            dst_f.write(data)
                            os.fsync(dst_f.fileno())
                    logger.warning(f"使用纯二进制模式复制FLY2文件 run{thread_id}")
                except Exception as binary_e:
                    logger.error(f"二进制复制FLY2文件也失败 run{thread_id}: {binary_e}")

    def _copy_excellib_file(self, thread_id, run_dir):
        """复制excellib.lua文件到指定线程目录"""
        excellib_file = self.base_dir / "excellib.lua"
        if excellib_file.exists():
            target_excellib = run_dir / "excellib.lua"
            try:
                with self._lock:  
                    with open(excellib_file, 'rb') as src_f:
                        data = src_f.read()
                    with open(target_excellib, 'wb') as dst_f:
                        dst_f.write(data)
                        os.fsync(dst_f.fileno())
            except Exception as e:
                logger.error(f"复制excellib.lua失败 run{thread_id}: {e}")

    def _create_pa_symlinks(self, thread_id, run_dir):
        """创建PA文件的符号链接"""
        try:
            for pa_file in self.common_dir.glob("*.pa"):
                target_file = run_dir / pa_file.name
                try:
                    if target_file.exists():
                        target_file.unlink()  
                    target_file.symlink_to(pa_file)  
                except Exception as e:
                    logger.warning(f"创建符号链接失败 {pa_file.name}: {e}")
                    try:
                        shutil.copy2(pa_file, target_file)
                    except Exception as e:
                        logger.error(f"复制PA文件失败 {pa_file.name}: {e}")
        except Exception as e:
            logger.error(f"创建PA符号链接失败 run{thread_id}: {e}")
            
        except Exception:
            return True  
    
    def _is_hardlink(self, file_path):
        """检查文件是否是硬链接（简化版本）"""
        try:
            if file_path.parent.parent == self.runs_dir:
                return True
            return False
        except Exception:
            return False
    
    def _get_directory_size(self, directory):
        """计算目录大小（字节）"""
        total_size = 0
        file_count = 0
        try:
            for dirpath, dirnames, filenames in os.walk(directory):
                for filename in filenames:
                    filepath = os.path.join(dirpath, filename)
                    try:
                        file_size = os.path.getsize(filepath)
                        total_size += file_size
                        file_count += 1
                    except (OSError, FileNotFoundError):
                        pass
            logger.info(f"扫描到 {file_count} 个文件")
        except Exception as e:
            logger.error(f"计算目录大小失败: {e}")
        return total_size
    
    def _format_size(self, size_bytes):
        """格式化文件大小"""
        if size_bytes == 0:
            return "0 B"
        
        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        while size_bytes >= 1024 and i < len(size_names) - 1:
            size_bytes /= 1024.0
            i += 1
        
        return f"{size_bytes:.1f} {size_names[i]}"
    
    def get_workspace_info(self):
        """获取工作空间信息"""
        return {
            'workspace_dir': str(self.workspace_dir),
            'common_dir': str(self.common_dir),
            'runs_dir': str(self.runs_dir),
            'thread_count': self.thread_count,
            'base_iob': str(self.base_iob_path),
            'electrode_count': self.get_electrode_count(),
            'pa_temp_extensions': self._get_pa_temp_extensions()
        }


def create_simion_workspace(iob_path, thread_count=None):
    """
    创建SIMION工作空间的便捷函数
    
    参数:
        iob_path: IOB文件路径
        thread_count: 线程数量
        
    返回:
        SimionWorkspaceManager实例
    """
    manager = SimionWorkspaceManager(iob_path, thread_count)
    if manager.setup_workspace():
        return manager
    else:
        return None
